package core;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;

import exception.ColumnException;
import exception.ColumnExistsException;
import exception.ConstraintException;
import exception.DataException;
import exception.NoSuchColumnFoundException;
import exception.NoSuchDatabaseFoundException;
import exception.NoSuchTableFoundException;
import exception.TableExistsException;
import exception.UnknownActionException;

public class Database {
	private String databaseName;
	private LinkedHashMap<String, Table> tables;
	
	public Database(String databaseName) {
		this.databaseName = databaseName;
		tables = new LinkedHashMap<String, Table>();
	}
	
	public void addTable(String tableName) throws TableExistsException {
		if(tables.containsKey(tableName)) {
			throw new TableExistsException("Table '"+ tableName +"' already exists");
		}
		
		tables.put(tableName, new Table(tableName));
	}
	
	public void addColumn(String tableName, String column) throws NumberFormatException, ColumnExistsException, ColumnException {
		Table table = tables.get(tableName);
	
		String[] attributes = column.split(":");
		if(attributes.length != 6) {
			throw new ColumnException("Column misscount, found "+ attributes.length +" columns but 6 are expected");
		}
		
		int position = 0;
		if(!attributes[1].isEmpty()) {
			position = Integer.parseInt(attributes[1]);
		}
		
		int characterLength = 0;
		if(!attributes[4].isEmpty()) {
			characterLength = Integer.parseInt(attributes[4]);
		}
		
		table.addColumn(attributes[0], position, attributes[2], attributes[3], characterLength, Boolean.parseBoolean(attributes[5]));
	}
	
	public void addColumn(String tableName, ArrayList<String> columns) throws NoSuchTableFoundException, ColumnExistsException, NumberFormatException, ColumnException {
		if(!tables.containsKey(tableName)) {
			throw new NoSuchTableFoundException("Table '"+ tableName +"' does not exists");
		}
		
		for(String column : columns) {
			addColumn(tableName, column);
		}
	}
	
	public void addConstraints(RDBMS rdbms, String tableName, ArrayList<String> constraints) throws NoSuchTableFoundException, NoSuchColumnFoundException, NoSuchDatabaseFoundException, ConstraintException {
		if(!tables.containsKey(tableName)) {
			throw new NoSuchTableFoundException("Table '"+ tableName +"' does not exists");
		}
		
		Table table = tables.get(tableName);
		for(String constraint : constraints) {
			table.addConstraint(rdbms, constraint);
		}
	}
	
	public String getDatabaseName() {
		return databaseName;
	}

	public Column getColumn(String table, String column) throws NoSuchTableFoundException, NoSuchColumnFoundException {
		if(!tables.containsKey(table)) {
			throw new NoSuchColumnFoundException("table "+ table +" does not exists");
		}
		
		return tables.get(table).getColumn(column);
	}
	
	// Temp
	public HashMap<String, Table> getTables() {
		return tables;
	}

	public void alterTable(String tableName, String action, String columnName, String type) throws NoSuchTableFoundException, NoSuchColumnFoundException, ColumnExistsException, UnknownActionException, ConstraintException, NumberFormatException, ColumnException {
		if(!tables.containsKey(tableName)) {
			throw new NoSuchTableFoundException("Table '"+ tableName +"' does not exists");
		}
		
		if(action.equals("ADD")) {
			addColumn(tableName, type);
		}else if(action.equals("DROP COLUMN")){
			tables.get(tableName).dropColumn(columnName);
		}else if(action.equals("ALTER COLUMN")){
			tables.get(tableName).alterColumn(columnName,type);
		}else{
			throw new UnknownActionException("Action "+action+" is not known");
		}
	}

	public void dropTable(RDBMS rdbms, String tableName) throws NoSuchTableFoundException, ConstraintException {
		if(!tables.containsKey(tableName)) {
			throw new NoSuchTableFoundException("Table '"+ tableName +"' does not exists");
		}
		
		if(rdbms.isReferenced(tables.get(tableName))){
			throw new ConstraintException("There is a reference to table "+tableName);
		}
		
		tables.remove(tableName);
	}

	public void insert(String tableName, HashMap<String, String> values) throws NoSuchTableFoundException, NoSuchColumnFoundException, DataException, ConstraintException {
		if(!tables.containsKey(tableName)) {
			throw new NoSuchTableFoundException("Table '"+ tableName +"' does not exists");
		}
		
		tables.get(tableName).insert(values);
	}
	
	public HashMap<String, String> select(String tableName, String columns, String[] conditions) throws NoSuchTableFoundException, NoSuchColumnFoundException {
		if(!tables.containsKey(tableName)) {
			throw new NoSuchTableFoundException("Table '"+ tableName +"' does not exists");
		}
		
		return tables.get(tableName).select(columns, conditions);
	}
	
	public void delete(String tableName, String[] conditions) throws NoSuchTableFoundException, NoSuchColumnFoundException {
		if(!tables.containsKey(tableName)) {
			throw new NoSuchTableFoundException("Table '"+ tableName +"' does not exists");
		}
		
		tables.get(tableName).delete(conditions);
	}
	
	public void update(String tableName, String[] update, String[] conditions) throws NoSuchTableFoundException, NoSuchColumnFoundException {
		if(!tables.containsKey(tableName)) {
			throw new NoSuchTableFoundException("Table '"+ tableName +"' does not exists");
		}
		
		tables.get(tableName).update(update, conditions);
	}
	
	public boolean isReferenced(Table table) {
		for(Table tab:tables.values()){
			if(tab != table){
				if(tab.isReferenced(table)){
					return true;
				}
			}
		}
		return false;
	}

	public int getNumberOfTables() {
		return tables.size();
	}
}
